package robato.logic;

import java.text.DecimalFormat;
import java.util.Hashtable;
import java.util.Observable;
import java.util.Vector;

public class Training extends Observable implements Runnable {
	int intNumComunicaciones = 0;
	public int intTotalMensajes = 0;
	int intTotalAciertos = 0;
	int intNumRobots = 0;
	int intIteraciones = 0;
	int intNumSymbols = 0;
	int intNumMeanings = 0;
	int iteracionActual = 0;
	boolean blnParar;
	public double pctCorrect;
	String strTypeTraining = "";
	Robot robots[];
	DecimalFormat decimalFormat;
	Vector<int[]> vectorParejas;
	Hashtable<Integer, Integer> tabuList;

	public Training() {
		this.decimalFormat = new DecimalFormat("#.##");
	}

	public synchronized void setParameters(Robot robots[], int iteraciones, String strType) {
		System.out.println("setParameters() Iniciado....");
		this.robots = robots;
		this.intIteraciones = iteraciones;
		this.strTypeTraining = strType;
		this.iteracionActual = 1;
		this.blnParar = false;

		this.intNumRobots = this.robots.length;
		this.intNumMeanings = this.robots[0].arrMeanings.length;
		this.intNumSymbols = this.robots[0].arrSymbols.length;

		if(this.strTypeTraining.equalsIgnoreCase("ALEATORIO")){
			this.intNumComunicaciones = this.intNumRobots * (this.intNumRobots - 1) / 2;
			this.intTotalMensajes = this.intNumComunicaciones * this.intNumMeanings;
		}
		else if(this.strTypeTraining.equalsIgnoreCase("QUORUM")){
			this.intNumComunicaciones = this.intNumRobots;
			this.intTotalMensajes = this.intNumComunicaciones * this.intNumMeanings;
		}
		
		this.setChanged();
		this.notifyObservers("Total Mensajes: " + this.intTotalMensajes + "\n");
		this.clearChanged();
		System.out.println("setParameters() Terminado....");
	}
	
	public synchronized void setParar(boolean bln) {
		System.out.println("setParar() Iniciado.... Value: "+bln);
		this.blnParar = bln;
		this.notify();
		System.out.println("setParar() Terminado.... ");
	}

	public void fillPairs() {
		this.vectorParejas = new Vector<int[]>();
		for (int message = 0; message < this.intNumComunicaciones; message++) {
			int intRamdonA = (int) (Math.random() * (this.intNumRobots));
			int intRamdonB = (int) (Math.random() * (this.intNumRobots));

			while (intRamdonA == intRamdonB) {
				intRamdonB = (int) (Math.random() * (this.intNumRobots));
			}

			if (this.containsPair(intRamdonA, intRamdonB)) {
				message--;
				continue;
			}
			int pareja[] = new int[2];
			pareja[0] = intRamdonA;
			pareja[1] = intRamdonB;
			this.vectorParejas.add(pareja);
		}
	}

	private boolean containsPair(int a, int b) {
		boolean blnRet = false;

		for (int i = 0; i < this.vectorParejas.size(); i++) {
			int pareja[] = this.vectorParejas.get(i);
			if (pareja[0] == a && pareja[1] == b) {
				blnRet = true;
			}
		}
		return blnRet;
	}

	public void quorumTraining() {
		System.out.println("quorumTraining() Iniciado....");
		
		int arrContador[] = new int[this.intNumSymbols];
		this.intTotalAciertos = 0;
		this.tabuList = new Hashtable<Integer, Integer>();

		for (int i = 0; i < this.intNumMeanings; i++) {
			
			if(this.blnParar)
				return;

			for (int j = 0; j < arrContador.length; j++) {
				arrContador[j] = 0;
			}

			for (int r = 0; r < this.robots.length; r++) {
				arrContador[this.robots[r].getMaxIndexMeaning(i)]++;
			}

			int maxIndex = 0;
			int numAtIndex = 0;

			for (int k = 0; k < arrContador.length; k++) {
				if (arrContador[k] >= numAtIndex) {
					maxIndex = k;
					numAtIndex = arrContador[k];
				}

			}
			//System.out.println("Significado: " + i + "\tIndice Maximo: " + maxIndex);
			for (int r = 0; r < this.robots.length; r++) {
				int intBeta = 0;
				boolean blnTabu = this.tabuList.containsKey(maxIndex);

				if (this.robots[r].getMaxIndexMeaning(i) == maxIndex && !blnTabu) {
					//System.out.println("ACIERTO!!!");
					intBeta = 1;
					this.intTotalAciertos++;
				} else {
					//System.out.println("FALLO!!!");
					intBeta = 0;
				}
			
				this.robots[r].updateConocimientoRow(intBeta, i, this.robots[r].getMaxIndexMeaning(i));
				this.setChanged();
				this.notifyObservers("UPDATE");
				this.clearChanged();
				//System.out.println(this.robots[r].toString());
			}
			this.tabuList.put(maxIndex, i);
		}
		this.pctCorrect = (double) (((double) this.intTotalAciertos / (double) this.intTotalMensajes) * 100);
		double point[] = new double[2];
		point[0] = (double) iteracionActual;
		point[1] = (double) intTotalAciertos;

		this.setChanged();
		this.notifyObservers(point);
		this.clearChanged();
		
		this.setChanged();
		this.notifyObservers("Iteracion: " + this.iteracionActual +"\tAciertos: "+this.intTotalAciertos+"\tPorcentaje:"+this.decimalFormat.format(this.pctCorrect)+ "%\n");
		this.clearChanged();

		if (this.intTotalMensajes == this.intTotalAciertos) {
			this.blnParar = true;
			this.setChanged();
			this.notifyObservers(true);
			this.clearChanged();
		}
		System.out.println("quorumTraining() Terminado....");
	}

	
	
	public void ramdonTraining() {
		System.out.println("ramdonTraining() Iniciado....");

		if (this.vectorParejas == null || this.vectorParejas.isEmpty()) {
			this.fillPairs();
		}
		
		this.intTotalAciertos = 0;
		for (int message = 0; message < this.vectorParejas.size(); message++) {
			if(this.blnParar)
				return;
			
			int a[] = vectorParejas.get(message);
			int intRamdonA = a[0];
			int intRamdonB = a[1];

			Robot robotA = robots[intRamdonA];
			Robot robotB = robots[intRamdonB];

			for (int i = 0; i < this.intNumMeanings; i++) {
				//System.out.println("Robots Aleatorios: Sender:" + robotA.getName() + "\tReceiber:" + robotB.getName()+ "\tSignificado: " + i);
				//System.out.println(robotA.toString() + robotB.toString());
				int indexMeaningA = robotA.getMaxIndexMeaning(i); // TODO Importante!
				//String meaningA = robotA.getMeaningAt(i);
				//String symbolA = robotA.getSymbolAt(indexMeaningA);

				int indexSymbolB = robotB.getMaxIndexSymbol(indexMeaningA); // TODO Aqui busco.
				//String meaningB = robotB.getMeaningAt(indexSymbolB);
				//String symbolB = robotB.getSymbolAt(indexMeaningA);

				//System.out.println("Significado A: " + meaningA + "\tSignificado B: " + meaningB);
				
				
				int intBeta = 0;
				if (i == indexSymbolB) {
					//System.out.println("ACIERTO!!!");
					intBeta = 1;
					this.intTotalAciertos++;
				} else {
					//System.out.println("FALLO!!!");
					intBeta = 0;
				}

				robotA.updateConocimientoRow(intBeta, i, indexMeaningA);
				robotB.updateConocimientoCol(intBeta, indexMeaningA, indexSymbolB);
				this.setChanged();
				this.notifyObservers("UPDATE");
				this.clearChanged();
				//System.out.println(robotA.toString() + robotB.toString());
			}

		}
		pctCorrect = (double) (((double) this.intTotalAciertos / (double) this.intTotalMensajes) * 100);
		double point[] = new double[2];
		point[0] = (double) this.iteracionActual;
		point[1] = (double) intTotalAciertos;
		
		this.setChanged();
		this.notifyObservers("Iteracion: " + this.iteracionActual +"\tAciertos: "+this.intTotalAciertos+"\tPorcentaje:"+this.decimalFormat.format(this.pctCorrect)+ "%\n");
		this.clearChanged();

		this.setChanged();
		this.notifyObservers(point);
		this.clearChanged();

		if (this.intTotalMensajes == this.intTotalAciertos) {
			blnParar = true;
			this.setChanged();
			this.notifyObservers(true);
			this.clearChanged();
		}

		System.out.println("ramdonTraining() Terminado....");
	}

	public void run() {
		System.out.println("run() Iniciado.... Tipo: " + this.strTypeTraining);
		while (this.iteracionActual <= this.intIteraciones) {
			synchronized (this) {
				while (this.blnParar) {
					try {
						System.out.println("Esperando.....");
						this.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
			if (this.strTypeTraining.equalsIgnoreCase("ALEATORIO")) {
				this.ramdonTraining();
			} else if (this.strTypeTraining.equalsIgnoreCase("QUORUM")) {
				this.quorumTraining();
			}
			this.iteracionActual++;
		}
		this.setChanged();
		this.notifyObservers(false);
		this.clearChanged();
		System.out.println("run() Terminado....");
	}

}
